﻿using System;
using System.Data.Linq;
using System.Linq;
using PhoneCore.Framework.Diagnostic.Tracing;
using PhoneCore.Framework.Storage;
using System.Collections.Generic;
using System.Linq.Expressions;
using SecureBox.UI.Infrastructure.Model;

namespace SecureBox.UI.Infrastructure.Data
{
    /// <summary>
    /// Represents a repository for Record entity which uses Linq DataContext
    /// </summary>
    public class RecordRepository: IRepository<Model.Record>
    {
        private readonly SecureBoxDataContext _dataContext;
        public RecordRepository(SecureBoxDataContext dataContext)
        {
            _dataContext = dataContext;
        }

        /// <summary>
        /// Add entity to repository
        /// </summary>
        /// <param name="entity"></param>
        public void Add(Model.Record entity)
        {
            //create new data record
            Data.Record dataRecord = new Data.Record() {Name = entity.Name, Date = entity.CreationDate, Description = entity.Description};

            //get template
            dataRecord.TemplateId = (from Data.Template t in _dataContext.Templates
                                     where t.Name == entity.Template.Name
                                     select t).Single().Id;

            //insert non-existed keywords
            var nonExistedKeywords = entity.Keywords
                .Where(k => !_dataContext.Keywords.Any(kd => kd.Name == k.Name))
                .ToList();
            nonExistedKeywords.ForEach(k=> _dataContext.Keywords.InsertOnSubmit(new Data.Keyword(){Name = k.Name}));
            _dataContext.SubmitChanges();
            //insert record-keyword
            var dataKeywords = entity.Keywords.Select(k => _dataContext.Keywords.Single(dk => dk.Name == k.Name));
            dataKeywords.ToList()
                .ForEach(dk => _dataContext.RecordKeywords.InsertOnSubmit(new RecordKeyword() { Keyword = dk, Record = dataRecord }));
            
            //insert properties
            var dataProperties = entity.Properties.Select(p => new Property() {Name = p.Name, Record = dataRecord, Value = p.Value});
            dataProperties.ToList().ForEach(_dataContext.Properties.InsertOnSubmit);

            //insert record
            _dataContext.Records.InsertOnSubmit(dataRecord);
            _dataContext.SubmitChanges();

        }

        /// <summary>
        /// Gets entity using predicate provided
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public Model.Record Get(Expression<Func<Model.Record, bool>> predicate)
        {
            return _dataContext.Records.ToList()
                .Select( r=> CreateFromData(r, _dataContext)).AsQueryable().Single(predicate);
        }

        /// <summary>
        /// Updates record in data base. NOTE: actually there is no update, remove/insert instead
        /// </summary>
        /// <param name="entity"></param>
        public void Update(Model.Record entity)
        {
            var dataRecord = _dataContext.Records.Single(r => r.Id ==entity.Id);
            _dataContext.Records.DeleteOnSubmit(dataRecord);

            Add(entity);
        }

        /// <summary>
        /// Removes entites from repository using predicate provided
        /// </summary>
        /// <param name="predicate"></param>
        public void Remove(Expression<Func<Model.Record, bool>> predicate)
        {
            var modelRecords = (from Data.Record r in _dataContext.Records
                                 select CreateFromData(r, _dataContext)).Where(predicate);

            var dataRecords = modelRecords.Select(r => _dataContext.Records.Single(rd => rd.Id == r.Id));
            _dataContext.Records.DeleteAllOnSubmit(dataRecords);
        }

        /// <summary>
        /// Returns all entities from repository
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Model.Record> List()
        {
 
            return (from Data.Record r in _dataContext.Records
                    select CreateFromData(r, _dataContext));
        }

        /// <summary>
        /// Returns entitites using predicate provided
        /// </summary>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public IEnumerable<Model.Record> List(Expression<Func<Model.Record, bool>> predicate)
        {
            return (from Data.Record r in _dataContext.Records
                    select CreateFromData(r, _dataContext)).Where(predicate);
        }

        /// <summary>
        /// Commit changes
        /// </summary>
        public void Commit()
        {
            _dataContext.SubmitChanges();
        }

        public static Model.Record CreateFromData(Data.Record record,SecureBoxDataContext dataContext)
        {
            var dataKeywords = record.RecordKeywords.Select(rk => dataContext.Keywords.Single(k => k.Id == rk.KeywordId));
            return new Model.Record()
            {
                Id = record.Id,
                Name = record.Name,
                Properties = record.Properties.Select(PropertyRepository.CreateFromData),
                Template = TemplateRepository.CreateFromData(dataContext.Templates.Single(t=>t.Id == record.TemplateId)),
                Keywords = dataKeywords.Select(KeywordRepository.CreateFromData),
                Description = record.Description,
                CreationDate = record.Date.HasValue?record.Date.Value:DateTime.MinValue
            };
        }
    }
}
